﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.ViewModel;
using System.Windows;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ZSens
{
    public abstract class Widget:NotificationObject
    {
        private int id;

        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        public abstract JObject jSerialize();

        public static Widget jDeserializeDispatch(JObject widget)
        {
            String type = (String) widget.GetValue("type");
            if (type == "Button")
            {
                return Button.jDeserialize(widget);
            }
            else if (type == "OrientationX")
            {
                return OrientationX.jDeserialize(widget);
            }
            else if (type == "OrientationY")
            {
                return OrientationY.jDeserialize(widget);
            }
            else return null;
        }

        public List<Action> BindedAction { get; set; }

        public Widget()
        {
            BindedAction = new List<Action>();
        }
        private string displayString;

        public string DisplayString
        {
            get { return displayString; }
            set { 
                displayString = value;
                this.RaisePropertyChanged("DisplayString");
            }
        }

        public string GetDisplayString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (Action action in BindedAction)
            {
                sb.AppendLine(action.GetDisplayString());
            }
            return sb.ToString().Trim();
        }

        public abstract object Clone();

        public abstract WidgetType GetWidgetType();

        public abstract int Serialize(byte[] data, int idx);

        public abstract int Update(byte[] data, int idx);
    }
    public interface BoolProvider
    {
        bool IsWidget();
        bool GetState();
        
    }
    public interface FloatProvider
    {
        bool IsWidget();
        float GetState();
    }
    
    public enum WidgetType
    {
        Button, ThrottleBar,
        OrientationX, OrientationY,
        Hat
    }
    public class Button
        : Widget,BoolProvider
    {
        public Point leftUP { get; private set; }
        public Point rightDown { get; private set; }
        public String text;
        bool State { get; set; }

        public static Button jDeserialize(JObject jWidget)
        {
            String text = (String)jWidget.GetValue("text");
            Point lt = new Point((double)((JObject)jWidget.GetValue("leftUp")).GetValue("X"), 
                (double)((JObject)jWidget.GetValue("leftUp")).GetValue("Y"));
            Point rb = new Point((double)((JObject)jWidget.GetValue("rightDown")).GetValue("X"), 
                (double)((JObject)jWidget.GetValue("rightDown")).GetValue("Y"));

            Button button = new Button(lt, rb, text);
            JArray actions = (JArray)jWidget.GetValue("BindedAction");
            foreach (JObject action in actions)
            {
                button.BindedAction.Add(Action.jDeserializeDispatch(action, button));
            }
            return button;
        }

        public override JObject jSerialize()
        {
            JObject button = new JObject();
            JArray actions = new JArray();
            JObject leftUpObject = new JObject();
            JObject rightDownObject = new JObject();

            leftUpObject.Add("X", leftUP.X);
            leftUpObject.Add("Y", leftUP.Y);
            rightDownObject.Add("X", rightDown.X);
            rightDownObject.Add("Y", rightDown.Y);

            button.Add("type", "Button");            
            button.Add("leftUp", leftUpObject);
            button.Add("rightDown", rightDownObject);
            button.Add("text", text);

            foreach (Action action in BindedAction)
            {
                actions.Add(action.jSerialize());
            }
            button.Add("BindedAction", actions);

            return button;
        }

        public bool IsWidget()
        {
            return true;
        }
        
        public Button(Point lu, Point rd, String text)
        {
            leftUP = lu;
            rightDown = rd;
            this.text = text;
            
        }
        public bool GetState()
        {
            return State;
        }

        public override WidgetType GetWidgetType()
        {
            return WidgetType.Button;
        }

        public override object Clone()
        {
            Button result = new Button(new Point(leftUP.X, leftUP.Y), new Point(rightDown.X, rightDown.Y), text);
            foreach (var item in BindedAction)
            {
                result.BindedAction.Add(item.PartialCopy(result));
            }
            return result;
        }

        public override int Update(byte[] data, int idx)
        {
            byte t = 0;
            idx = Serializing.readFromByteArray(data, idx, ref t);
            if (idx < 0)
                return -1;
            State = t == 0 ? false : true;
            return idx;
        }

        public override int Serialize(byte[] data, int idx)
        {
            idx = Serializing.writeIntoByteArray(data, idx, (byte)GetWidgetType());
            idx = Serializing.writeIntoByteArray(data, idx, text);
            idx = Serializing.writeIntoByteArray(data, idx, (int) leftUP.X);
            idx = Serializing.writeIntoByteArray(data, idx, (int) leftUP.Y);
            idx = Serializing.writeIntoByteArray(data, idx, (int) rightDown.X);
            return Serializing.writeIntoByteArray(data, idx, (int) rightDown.Y);
        }
    }
    public class ThrottleBar
        : Widget, FloatProvider
    {
        public bool IsWidget()
        {
            return true;
        }
        public float GetState()
        {
            return 0;
        }
        public override JObject jSerialize()
        {
            throw new NotImplementedException();
        }

        public override WidgetType GetWidgetType()
        {
            return WidgetType.ThrottleBar;
        }

        public override object Clone()
        {
            ThrottleBar result = new ThrottleBar();
            foreach (var item in BindedAction)
            {
                result.BindedAction.Add(item.PartialCopy(result));
            }
            return result;
        }
        public override int Update(byte[] data, int idx)
        {
            throw new NotImplementedException();
        }

        public override int Serialize(byte[] data, int idx)
        {
            throw new NotImplementedException();
        }
    }
    public abstract class Orientation
        : Widget, FloatProvider
    {
        public bool IsWidget()
        {
            return true;
        }
        public float GetState()
        {
            return State;
        }
        protected float State { get; set; }
        
        public override int Serialize(byte[] data, int idx)
        {
            return Serializing.writeIntoByteArray(data, idx, (byte)GetWidgetType());
        }
    }
    public sealed class OrientationX
        : Orientation
    {
        public static OrientationX jDeserialize(JObject jWidget)
        {
            OrientationX orientationX = new OrientationX();
            JArray actions = (JArray)jWidget.GetValue("BindedAction");
            foreach (JObject action in actions)
            {
                orientationX.BindedAction.Add(Action.jDeserializeDispatch(action, orientationX));
            }
            return orientationX;
        }

        public override JObject jSerialize()
        {
            JObject orientationX = new JObject();
            JArray actions = new JArray();
            orientationX.Add("type", "OrientationX");
            foreach (Action action in BindedAction)
            {
                actions.Add(action.jSerialize());
            }
            orientationX.Add("BindedAction", actions);
            return orientationX;
        }
        
        public override WidgetType GetWidgetType()
        {
            return WidgetType.OrientationX;
        }

        public override object Clone()
        {
            OrientationX result = new OrientationX();
            foreach (var item in BindedAction)
            {
                result.BindedAction.Add(item.PartialCopy(result));
            }
            return result;
        }

        public override int Update(byte[] data, int idx)
        {
            int t = 0;
            idx = Serializing.readFromByteArray(data, idx, ref t);
            if (idx < 0)
                return -1;
            State = new Range { Start = 90.0f, End = -90.0f }.Normalize((float) t);
            return idx;
        }
    }
    public sealed class OrientationY
        : Orientation
    {
        public static OrientationY jDeserialize(JObject jWidget)
        {
            OrientationY orientationY = new OrientationY();
            JArray actions = (JArray)jWidget.GetValue("BindedAction");
            foreach (JObject action in actions)
            {
                orientationY.BindedAction.Add(Action.jDeserializeDispatch(action, orientationY));
            }
            return orientationY;
        }

        public override JObject jSerialize()
        {
            JObject orientationY = new JObject();
            JArray actions = new JArray();
            orientationY.Add("type", "OrientationY");
            foreach (Action action in BindedAction)
            {
                actions.Add(action.jSerialize());
            }
            orientationY.Add("BindedAction", actions);
            return orientationY;
        }

        public override WidgetType GetWidgetType()
        {
            return WidgetType.OrientationY;
        }

        public override object Clone()
        {
            OrientationY result = new OrientationY();
            foreach (var item in BindedAction)
            {
                result.BindedAction.Add(item.PartialCopy(result));
            }
            return result;
        }
        
        public override int Update(byte[] data, int idx)
        {
            int t = 0;
            idx = Serializing.readFromByteArray(data, idx, ref t);
            if (idx < 0)
                return idx;
            State = new Range { Start = -90.0f, End = 90.0f }.Normalize((float)t);
            return idx;
        }
    }
    public sealed class Hat
        : Widget, FloatProvider
    {
        public bool IsWidget()
        {
            return true;
        }
        float State { get; set; }

        public float GetState()
        {
            return State;
        }
        public override WidgetType GetWidgetType()
        {
            return WidgetType.Hat;
        }
        public override object Clone()
        {
            Hat result = new Hat();
            foreach (var item in BindedAction)
            {
                result.BindedAction.Add(item.PartialCopy(result));
            }
            return result;
        }

        public override int Update(byte[] data, int idx)
        {
            throw new NotImplementedException();
        }

        public override JObject jSerialize()
        {
            throw new NotImplementedException();
        }

        public override int Serialize(byte[] data, int idx)
        {
            throw new NotImplementedException();
        }
    }
}
